﻿using System.Collections.Generic;
using System.IO;
using System;

namespace Game.Lib.CSG
{
    public class Polygon : Shape2D
    {
        public override List<Vector> CriticalPoints
        {
            get
            {
                List<Vector> ret = new List<Vector>(Points.Count + 1);
                ret.Add(Pos);
                ret.AddRange(Points);
                return ret;
            }
        }
        public override Vector2 Pos
        {
            get
            {
                Vector2 sum = Vector2.Zero;
                foreach (Vector2 p in Points) sum += p;
                return sum / Points.Count;
            }
            set
            {
                Vector2 delta = value - Pos;
                List<Vector2> replace = new List<Vector2>(Points);
                replace.ForEach(x => x += delta);
                Points = replace;
            }
        }

        public override Vector2 Size
        {
            get
            {
                Vector2 min = Points[0];
                Vector2 max = Points[0];
                foreach (Vector2 p in Points)
                {
                    min = Vector2.Min(min, p);
                    max = Vector2.Max(max, p);
                }
                return max - min;
            }
            set
            {
                Vector2 size = Size;
                float scx = value.X / size.X;
                float scy = value.Y / size.Y;

                foreach (Vector2 p in Points)
                {
                    p.X *= scx;
                    p.Y *= scy;
                }
            }
        }

        public override float Rot
        {
            get
            {
                return _Rot;
            }
            set
            {
                _Rot += value;
                List<Vector2> polars = new List<Vector2>(Points);
                Vector2 pos = Pos;
                Vector2 delta = new Vector2(0f, value);
                polars.ForEach(x => x = Vector2.FromPolar((x - pos).ToPolar() + delta));
                Points = polars;
            }
        }

        public List<Vector2> Points;

        public List<LineSegment> Sides
        {
            get
            {
                List<LineSegment> ret = new List<LineSegment>(Points.Count);
                for (int i = 1; i < Points.Count; i++) ret.Add(new LineSegment(Points[i - 1], Points[i]));
                ret.Add(new LineSegment(Points[Points.Count - 1], Points[0]));
                return ret;
            }
        }

        public List<Triangle> Triangles
        {
            get
            {
                //There are some precision issues with the standard "all tris share one point of the polygon as a vertice" model
                //So we'll make a new point, and make a tri-fan
                Vector2 p = Pos;
                List<Triangle> ret = new List<Triangle>(Points.Count);
                for (int i = 1; i < Points.Count; i++) ret.Add(new Triangle(p, Points[i - 1], Points[i]));
                ret.Add(new Triangle(p, Points[Points.Count - 1], Points[0]));
                return ret;
            }
        }

        public override float Area
        {
            get
            {
                float ret = 0f;
                foreach (Triangle t in Triangles) ret += t.Area;
                return ret;
            }
        }

        public override float Perimeter
        {
            get
            {
                float ret = 0f;
                foreach (LineSegment ls in Sides) ret += ls.Length;
                return ret;
            }
        }

        private float _Rot;

        public override Rectangle GetBoundingRectangle()
        {
            return new Rectangle(Pos, Size);
        }

        public override Ellipse GetBoundingCircle()
        {
            return new Ellipse(Pos, Size.Magnitude() * .5f);
        }

        public override bool Inside(Vector2 pos)
        {
            LineSegment fwd = new LineSegment(pos, pos + new Vector2(float.PositiveInfinity, 0f));
            int crosses = 0;
            foreach(LineSegment ls in Sides) if(LineSegment.Intersection(ls, fwd) != null) crosses++;
            return crosses % 2 == 1;
        }

        public override void Serialize(Stream s)
        {
            if (!s.CanWrite) throw new ArgumentException("Stream must be writable for serialization");
            s.Write(BitConverter.GetBytes((ushort)Points.Count), 0, 4);
            foreach (Vector2 p in Points) p.Serialize(s);
        }

        public override void Deserialize(Stream s)
        {
            byte[] buf = new byte[4];
            if (s.Read(buf, 0, 4) != 4) throw new EndOfStreamException();
            int cnt = BitConverter.ToUInt16(buf, 0);
            Points = new List<Vector2>(cnt);
            for (int i = 0; i < cnt; i++)
            {
                Vector2 temp = new Vector2();
                temp.Deserialize(s);
                Points.Add(temp);
            }
        }
    }
}
